/*
 * Copyright (C) 2011-2018 ShenZhen iBOXSaaS Information Technology Co.,Ltd.
 * 
 * All right reserved.
 * 
 * This software is the confidential and proprietary information of iBOXSaaS Company of China.
 * ("Confidential Information"). You shall not disclose such Confidential Information and shall use it only in
 * accordance with the terms of the contract agreement you entered into with iBOXSaaS inc.
 * 
 */

package com.iboxpay.open.gateway.filter;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.iboxpay.open.common.constants.LogEnums;
import org.iboxpay.open.common.constants.ParamEnums;
import org.iboxpay.open.common.constants.ResultEnums;
import org.iboxpay.open.common.exception.BusinessException;
import org.iboxpay.open.common.utils.SnowflakeIdWorker;
import org.iboxpay.open.common.vo.BaseResponseVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import com.iboxpay.common.json.MapperUtils;
import com.iboxpay.open.gateway.service.TokenValidateService;
import com.iboxpay.open.gateway.utils.ParamUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

import io.jsonwebtoken.Claims;

/**
 * 
 * AccessTokenFilter.java
 * 
 * @author xuxiao
 * @date 2018/06/15
 */
@Component
public class AccessTokenFilter extends ZuulFilter {

    private static final Logger interfaceLoger = LoggerFactory.getLogger("interface");
    
    private static final Logger remoteLoger = LoggerFactory.getLogger("client-interface");
    
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    private TokenValidateService tokenValidateService;

    @Override
    public Object run() {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String requestUri = request.getRequestURI();

        // 日志打印
        MDC.put(LogEnums.transactionId.getCode(), String.valueOf(snowflakeIdWorker.nextId()));
        MDC.put(LogEnums.startTime.getCode(), String.valueOf(System.currentTimeMillis()));
        interfaceLoger.info("[{}] [HTTP] [request] - {} {}", requestUri, request.getMethod(),
            MapUtils.isEmpty(request.getParameterMap()) ? ParamUtils.getRequestBody(request) : MapperUtils.toJson(request.getParameterMap()));
        // token验证
        try {
            Claims claims = tokenValidateService.tokenValidate(request);
            if(claims != null) {
                // 往请求头中添加userId
                Long openId = claims.get(ParamEnums.openId.getCode(), Long.class);
                String userId = tokenValidateService.getUserId(openId);
                if(StringUtils.isNotBlank(userId)) {
                    context.addZuulRequestHeader(ParamEnums.userId.getCode(), userId);
                }
            }
            remoteLoger.info("[{}] [HTTP] [request] - {} {}", requestUri, request.getMethod(),
                MapUtils.isEmpty(request.getParameterMap()) ? ParamUtils.getRequestBody(request) : MapperUtils.toJson(request.getParameterMap()));
        } catch (BusinessException e) {
            setRequestContext(context, false, 401, e.getResultEnums());
        } catch (Exception e) {
            setRequestContext(context, false, 401, ResultEnums.tokenSystemError);
        }

        return null;
    }

    /**
     * 设置返回值
     * 
     * @param context 上下文
     * @param sendZuulResponse zuul响应状态
     * @param responseStatusCode 响应码
     * @param resultCode 错误码
     * @param errorDesc 错误描述
     */
    private void setRequestContext(RequestContext context, boolean sendZuulResponse, int responseStatusCode,
        ResultEnums resultEnums) {
        context.getResponse().setContentType("application/json;charset=UTF-8");
        context.setSendZuulResponse(sendZuulResponse);
        context.setResponseStatusCode(responseStatusCode);
        BaseResponseVo response = new BaseResponseVo();
        response.setResultCode(resultEnums.getCode());
        response.setErrorDesc(resultEnums.getDesc());
        context.setResponseBody(response.toString());
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
}
